package com.surge;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class SurgeTest {
	
	private SessionFactory sessionFactory;
	// 生产环境不允许 把session与transaction 设置成成员变量，因为涉及到并发问题
	private Session session;
	private Transaction transaction;
	
	// junit 方法init 与 destory。 这样只需要test中写主要的业务测试逻辑
	@Before
	public void init(){
		//1、 版本5.x	
		final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
				.configure() // configures settings from hibernate.cfg.xml
				.build();
		try {
			sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
		}
		catch (Exception e) {
			StandardServiceRegistryBuilder.destroy( registry );
		}
		// 2. 创建一个 Session 对象
		session = sessionFactory.openSession();

		// 3. 开启事务
		transaction = session.beginTransaction();
	}
	@After
	public void destory(){
		// 5. 提交事务
		transaction.commit();

		// 6. 关闭 Session
		session.close();

		// 7. 关闭 SessionFactory 对象
		sessionFactory.close();
	}
	
	/*
	 * ============================    测试逻辑代码    ============================
	 */
	
	/*
	 *  多对一/一对多  双向关联
	 */	
	
	/**
	 * 先插入 1 的一端
	 */
	@Test
	public void testOne2ManySave(){
		Customer customer = new Customer();
		customer.setCustomerName("AA");
		
		Order order1 = new Order();
		order1.setOrderName("ORDER-1");
		
		Order order2 = new Order();
		order2.setOrderName("ORDER-2");
		
		//设定关联关系
		order1.setCustomer(customer);
		order2.setCustomer(customer);
		
		customer.getOrders().add(order1);
		customer.getOrders().add(order2);
		
		/*
		 * set节点没有 inverse="true"
		 * 1、 执行  save 操作: 先插入 Customer, 再插入 Order, 3 条 INSERT, 2 条 UPDATE
		 *    因为 1 的一端和 n 的一端都维护关联关系. 所以会多出 UPDATE
		 */
//		session.save(customer);
//		session.save(order1);
//		session.save(order2);
		
		/* set节点设置 inverse="true"
		 * 2、可以在 1 的一端的 set 节点指定 inverse=true （被动方）, 来使 1 的一端放弃维护关联关系!
		 *   （一的一端 数据表中并没有关联字段）
		 *    建议设定 set 的 inverse=true, 建议先插入 1 的一端, 后插入多的一端
		 *    好处是不会多出 UPDATE 语句
		 */
		session.save(customer); // 发送三条Insert语句
		session.save(order1);
		session.save(order2);
		
	}
	/**
	 * 先插入 多 的一端
	 */
	@Test
	public void testOne2ManySave2(){
		Customer customer = new Customer();
		customer.setCustomerName("AA");
		
		Order order1 = new Order();
		order1.setOrderName("ORDER-1");
		
		Order order2 = new Order();
		order2.setOrderName("ORDER-2");
		
		//设定关联关系
		order1.setCustomer(customer);
		order2.setCustomer(customer);
		
		customer.getOrders().add(order1);
		customer.getOrders().add(order2);
		
		// 先插入 Order, 再插入 Cusomer, 3 条 INSERT, 4 条 UPDATE（两张表都需要update关联关系）
//		session.save(order1);
//		session.save(order2);
//		session.save(customer);
		
	}

	
	/**
	 * 查询
	 */
	@Test
	public void testOne2ManyGet3(){
		//1. 对 n 的一端的集合使用延迟加载
		Customer customer = (Customer) session.get(Customer.class, 6);
		System.out.println(customer.getCustomerName()); 
		//2. 返回的多的一端的集合时 Hibernate 内置的集合类型. 
		// 该类型具有延迟加载和存放代理对象的功能. 
		System.out.println(customer.getOrders().getClass()); 
		
		//session.close();
		//3. 可能会抛出 LazyInitializationException 异常 
		
		System.out.println(customer.getOrders().size()); 
		
		//4. 再需要使用集合中元素的时候进行初始化. 
	}
	
	/**
	 *  更新
	 */
	@Test
	public void testUpdat2(){
		// 两条select 一条update
		Customer customer = (Customer) session.get(Customer.class, 1);
		// order集合的第二条记录更新GGG
		customer.getOrders().iterator().next().setOrderName("GGG"); 
	}
	
	/**
	 *  删除
	 */
	@Test
	public void testDelete(){
		//在不设定级联关系的情况下, 且 1 这一端的对象有 n 的对象在引用, 不能直接删除 1 这一端的对象
		Customer customer = (Customer) session.get(Customer.class, 1); 
		session.delete(customer);  // 不能删除，因为 还有order引用他。报错
		
		// 可以设置级联删除属性  set节点 cascade="delete" 这样删除一，多的部分也会删除
	}
	
	/**
	 * 删除 多的部分
	 */
	@Test
	public void testCascade(){
		Customer customer = (Customer) session.get(Customer.class, 3);
		/*
		 *  清空order，就是说解除 与 Customer的关联
		 *  在set节点设置 cascade="delete-orphan"
		 */
		customer.getOrders().clear(); 
	}
	
}
